package com.sixbro.oauth2.api;

import com.sixbro.oauth2.constant.AuthProperties;
import com.sixbro.oauth2.security.domain.OAuth2UserDetails;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import java.security.Principal;

/**
 * @Author: Mr.Lu
 * @Since: 2020/6/14 22:57
 */
@Controller
@Log4j2
public class AuthenticationController {

    @Autowired
    private ConsumerTokenServices consumerTokenServices;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private PasswordEncoder encoder;

    @Autowired
    private UserDetailsService userDetailsService;

    /**
     * 欢迎页面
     * @param model
     * @return
     */
    @GetMapping(path = {"/"})
    public String welcome(Model model) {
        model.addAttribute("action", AuthProperties.LOGIN_PAGE);
        return "index";
    }

    /**
     * 认证页面
     * @return ModelAndView
     */
    @GetMapping(path = { "/login", AuthProperties.LOGIN_PAGE})
    public ModelAndView login() {
        log.info("---认证页面---");
        ModelAndView view = new ModelAndView();
        view.addObject("action", AuthProperties.LOGIN_PROCESS_URL);
        view.addObject("username", AuthProperties.USERNAME_PARAMETER_NAME);
        view.addObject("password", AuthProperties.PASSWORD_PARAMETER_NAME);
        view.setViewName("login");
        return view;
    }

    /**
     * 退出时将token清空（使用RedisStore时就是删除掉对应缓存）
     * @param authorization
     * @return
     */
    @DeleteMapping("/logout")
    @ResponseBody
    public String logout(@RequestHeader(value = "Authorization") String authorization) {
        String accessToken = authorization.substring(OAuth2AccessToken.BEARER_TYPE.length()).trim();
        consumerTokenServices.revokeToken(accessToken);
        return "ok";
    }

    @GetMapping("/index")
    @ResponseBody
    public String home(@AuthenticationPrincipal Principal principal) {
        return "Home of user: " + principal.getName();
    }

    @GetMapping("/user")
    public @ResponseBody Object userInfo(Principal user) {
        log.info("user:{}",user);
        return  user;
    }

    /**
     * 更新用户信息时更新redis中的用户信息
     * @param authentication
     * @return java.lang.String
     */
    @GetMapping("/update")
    @ResponseBody
    public String updateCacheUserInfo(Authentication authentication) {
        if (authentication instanceof OAuth2Authentication) {
            OAuth2Authentication auth2Authentication = (OAuth2Authentication) authentication;
            Authentication userAuthentication = auth2Authentication.getUserAuthentication();
            OAuth2Authentication newOAuth2Authentication = null;
            if (userAuthentication instanceof UsernamePasswordAuthenticationToken) {
                OAuth2UserDetails auth2User = (OAuth2UserDetails) userDetailsService.loadUserByUsername("yaohw");
                auth2User.setUsername("yaohw2");
                auth2User.setTest("test333");
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(auth2User.getUsername(), auth2User.getPassword(), auth2User.getAuthorities());
                newOAuth2Authentication= new OAuth2Authentication(auth2Authentication.getOAuth2Request(),usernamePasswordAuthenticationToken);
            }
            OAuth2AccessToken accessToken = tokenStore.getAccessToken(auth2Authentication);
            if (newOAuth2Authentication != null) {
                tokenStore.storeAccessToken(accessToken,newOAuth2Authentication);
            }
        }
        return "ok";
    }

    @GetMapping("/encrypt")
    @ResponseBody
    public String encrypt( String content ) {
        return encoder.encode(content);
    }

    /**
     * 不需要token访问测试
     * @return
     */
    @GetMapping("/test/no_need_token")
    public @ResponseBody String test() {
        return "no_need_token";
    }

    /**
     * 需要token访问接口测试
     * @return
     */
    @GetMapping("/test/need_token")
    public @ResponseBody String test2() {
        return "need_token";
    }

    /**
     * 需要需要管理员权限
     * @return
     */
    @PreAuthorize("hasAuthority('admin')")
    @GetMapping("/test/need_admin")
    public @ResponseBody String admin() {
        return "need_admin";
    }

    /**
     * scope 控制测试,该方法只有配置有scope为sever2的客户端能访问，针对的是客户端
     * @return
     */
    @GetMapping("/test/scope")
    @PreAuthorize("#oauth2.hasScope('sever2')")
    public @ResponseBody String test3() {
        return "scope-test";
    }

}
